#!/bin/bash
exec >trafficgen-server-start-stderrout.txt 
exec 2>&1

. /usr/bin/trafficgen-base || (echo "/usr/bin/trafficgen-base not found"; exit 1)

dump_runtime
validate_label

# Defaults
sample_dir=`/bin/pwd`
devices=""
testpmd_forward_mode="mac"
testpmd_queues="1"
testpmd_descriptors="2048"
testpmd_smt_mode="grouped"
testpmd_smt="on"
testpmd_mtu="1518"
testpmd_enable_scatter="off"
testpmd_enable_rx_cksum="off"
testpmd_enable_rss_udp="off"
testpmd_devopt=""
testpmd_mbuf_size=""
testpmd_burst=""

# Options processing
re='^(--[^=]+)=([^=]+)'
while [ $# -gt 0 ]; do
    if [[ "$1" =~ $re ]]; then
        arg="${BASH_REMATCH[1]}"
        val="${BASH_REMATCH[2]}"
        shift
    else
        arg="$1"
        shift
        val="$1"
        shift
    fi
    case "$arg" in
        # The following two are needed to determine DPDK device IDs (0,1,2...)
        --devices)
            devices="$val"
            ;;
        --testpmd-forward-mode)
            testpmd_forward_mode="$val"
            ;;
        --testpmd-queues)
            testpmd_queues="$val"
            ;;
        --testpmd-descriptors)
            testpmd_descriptors="$val"
            ;;
        --testpmd-smt-mode)
            testpmd_smt_mode="$val"
            ;;
        --testpmd-smt)
            testpmd_smt="$val"
            ;;
        --testpmd-mtu)
            testpmd_mtu="$val"
            ;;
        --testpmd-devopt)
            testpmd_devopt="$val"
            ;;
        --testpmd-enable-scatter)
            testpmd_enable_scatter="$val"
            ;;
        --testpmd-enable-rx-cksum)
            testpmd_enable_rx_cksum="$val"
            ;;
        --testpmd-enable-rss-udp)
            testpmd_enable_rss_udp="$val"
            ;;
        --testpmd-mbuf-size)
            testpmd_mbuf_size="$val"
            ;;
        --testpmd-burst)
            testpmd_burst="$val"
            ;;
    esac
done

if [ -z "$devices" ]; then
    exit_error "Value for --server-devices was not found, exiting" 1 "$sample_dir"
fi

# If --server-devices is "none", then this indicates that the client
# is not testing a device-under-test, but is confgiured to be in 
# loopback: the traffic-generators' interfaces are connected directly
# to itself.  This is used for sanity checks.
if [ "$devices" == "none" ]; then
    echo "Will not start testpmd because this is a traffic-generator loopback test"
    exit 0
fi

# trafficgen-server may receive a message about MACs from the traffic-generator
echo "These files exist in ./msgs/rx:"
/bin/ls -l msgs/rx
file="msgs/rx/server-start:1"
if [ -e "$file" ]; then
    echo "Found $file:"
    cat $file
    peermac0=`jq -r '.macs[0]' $file`
    if [ ! -z "$peermac0" ]; then
        echo "Found eth-peer MAC0 $peermac0"
    fi
    peermac1=`jq -r '.macs[1]' $file`
    if [ ! -z "$peermac1" ]; then
        echo "Found eth-peer MAC1 $peermac1"
    fi
fi

# Build testpmd cmdline opts for device selection
echo "Resolving devices for testpmd based on devices [$devices]"
testpmd_dev_opt=""
for dev in `echo $devices | sed -e 's/,/ /g'`; do
    res_dev=""
    resolve_device res_dev $dev
    testpmd_dev_opt+=" --allow $res_dev"
    if [ ! -z "$testpmd_devopt" ]; then
        testpmd_dev_opt+=",$testpmd_devopt"
    fi
done
if [ -z "$testpmd_dev_opt" ]; then
    exit_error "Testpmd devices could not be found, exiting" 1 "$sample_dir"
fi

# figure out the CPUs to use
if [ -z "${HK_CPUS}" ] ; then
    exit_error "There are no housekeeping CPUs to use for testpmd" 1 "$sample_dir"
else
    echo "HK_CPUS=${HK_CPUS}"
fi
HK_CPUS_SEPARATED=$(echo "${HK_CPUS}" | sed -e "s/,/ /g")
HK_CPUS_ARRAY=(${HK_CPUS_SEPARATED})
if [ ${#HK_CPUS_ARRAY[@]} -lt 1 ]; then
    exit_error "You must have at least 1 HK_CPUS" 1 "$sample_dir"
fi

if [ -z "${WORKLOAD_CPUS}" ]; then
    exit_error "There are no dedicated/isolated CPUs to use for testpmd" 1 "$sample_dir"
else
    echo "WORKLOAD_CPUS=${WORKLOAD_CPUS}"
fi
WORKLOAD_CPUS_SEPARATED=$(echo "${WORKLOAD_CPUS}" | sed -e "s/,/ /g")

cpu_str=""
for cpu in $WORKLOAD_CPUS_SEPARATED; do
    cpu_str+=" --cpu $cpu"
done
cmd="${TOOLBOX_HOME}/bin/get-cpus-ordered.py --smt ${testpmd_smt} --smt-enumeration ${testpmd_smt_mode} ${cpu_str}"
echo "about to run: ${cmd}"
CMD_OUTPUT=$(${cmd})
echo -e "${CMD_OUTPUT}"
WORKLOAD_CPUS_SEPARATED=$(echo -e "${CMD_OUTPUT}" | grep "final cpus:" | awk '{ print $3 }' | sed -e "s/,/ /g")
WORKLOAD_CPUS_ARRAY=(${WORKLOAD_CPUS_SEPARATED})

testpmd_cores=${#WORKLOAD_CPUS_ARRAY[@]}
testpmd_cpus=""
cpu_idx=0
for cpu in $(echo "${HK_CPUS_ARRAY[0]} ${WORKLOAD_CPUS_SEPARATED}" | sed -e "s/\s+/ /g" -e "s/^\s//" -e "s/\s$//"); do
    testpmd_cpus+="${cpu_idx}@${cpu},"
    (( cpu_idx++ ))
done
testpmd_cpus=$(echo "${testpmd_cpus}" | sed -e "s/,$//")

# autodetect what the testpmd numa memory configuration should be
if pushd /sys/devices/system/node > /dev/null; then
    testpmd_mem=""

    for node in $(ls -1d node*); do
        NODE_NUM=$(echo ${node} | sed -e "s/node//")
        if pushd $node > /dev/null; then
            NODE_CPU_PRESENT=0

            for cpu in ${WORKLOAD_CPUS_SEPARATED}; do
                if [ -d "cpu${cpu}" ]; then
                    NODE_CPU_PRESENT=1
                    break
                fi
            done

            if [ "${NODE_CPU_PRESENT}" == "1" ]; then
                testpmd_mem+="1024,"
            else
                testpmd_mem+="0,"
            fi

            popd > /dev/null
        fi
    done

    testpmd_mem=$(echo "${testpmd_mem}" | sed -e "s/,$//")

    popd > /dev/null
fi

# if the memory autodetect code failed for some reason, try to use a
# safe default and hope it works
if [ -z "${testpmd_mem}" ]; then
    testpmd_mem="1024,1024"
fi

testpmd_output="trafficgen-testpmd-stderrout.txt"
testpmd_bin=$( command -v dpdk-testpmd || command -v testpmd )
testpmd_opts=" --lcores ${testpmd_cpus}"
testpmd_opts+=" --socket-mem ${testpmd_mem}"
testpmd_opts+=" --huge-dir /dev/hugepages $testpmd_dev_opt"
testpmd_opts+=" -v"
testpmd_opts+=" --"
testpmd_opts+=" --nb-ports 2 --nb-cores ${testpmd_cores} --auto-start --stats-period=5"
testpmd_opts+=" --rxq ${testpmd_queues} --txq ${testpmd_queues}"
testpmd_opts+=" --rxd ${testpmd_descriptors} --txd ${testpmd_descriptors}"
testpmd_opts+=" --max-pkt-len=${testpmd_mtu}"
testpmd_opts+=" --record-core-cycles"
testpmd_opts+=" --record-burst-stats"
if [ "$testpmd_enable_scatter" == "on" ]; then
    testpmd_opts+=" --enable-scatter"
elif [ "$testpmd_enable_scatter" == "off" ]; then
    echo "--enable-scatter will not be used"
else
    exit_error "value for --testpmd-enable-scatter ($testpmd_enable_scatter) is not valid.  Use either 'on' or 'off'"
fi

if [ "$testpmd_enable_rx_cksum" == "on" ]; then
    testpmd_opts+=" --enable-rx-cksum"
elif [ "$testpmd_enable_rx_cksum" == "off" ]; then
    echo "--enable-rx-cksum will not be used"
else
    exit_error "value for --testpmd-enable-rx-cksum ($testpmd_enable_rx_cksum) is not valid.  Use either 'on' or 'off'"
fi

if [ "$testpmd_enable_rss_udp" == "on" ]; then
    testpmd_opts+=" --rss-udp"
elif [ "$testpmd_enable_rss_udp" == "off" ]; then
    echo "--enable-rss-udp will not be used"
else
    exit_error "value for --testpmd-enable-rss-udp ($testpmd_enable_rss_udp) is not valid.  Use either 'on' or 'off'"
fi

if [ ! -z "$testpmd_burst" ]; then
    testpmd_opts+=" --burst=$testpmd_burst"
fi

# Bump up the default mbuf if the MTU is large,
# but allow a user-defined mbuf size to not be changed
if [ "${testpmd_mtu}" -gt 2048 -a -z "$testpmd_mbuf_size" ]; then
    testpmd_mbuf_size=16384
    testpmd_opts+=" --mbuf-size=${testpmd_mbuf_size}"
fi

if [ ! -z "$testpmd_mbufs" ]; then
    testpmd_opts+=" --total-num-mbufs=${testpmd_mbufs}"
fi

if [ "$testpmd_forward_mode" == "mac" ]; then
    # TODO: use regex instead:
    if [ -z "$peermac0" -o -z "$peermac1" ]; then
        exit_error  "[ERROR] Using forware-mode = mac, but did not get MAC addresses from TREX" 1 "$sample_dir"
    fi
    testpmd_opts+=" --eth-peer 0,$peermac0 --eth-peer 1,$peermac1 --forward-mode mac"
else
    testpmd_opts+=" "
fi

echo "Going to run: $testpmd_bin $testpmd_opts"
$testpmd_bin $testpmd_opts 2>&1 >$testpmd_output &
echo $! >> trafficgen-server.pid
sleep 5 # TODO: need a better wait
mac0=`egrep "Port 0: [A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}" $testpmd_output | awk -F"0: " '{print $2}'`
mac1=`egrep "Port 1: [A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}:[A-Fa-f0-9]{2}" $testpmd_output | awk -F"1: " '{print $2}'`
echo "MAC0: $mac0"
echo "MAC1: $mac1"
# TODO: fail if MACs are not found in testpmd output
if [ "$testpmd_forward_mode" == "mac" ]; then
    # MAC info, to be sent to endpoint and then forwarded to the client
    echo '{"recipient":{"type":"all","id":"all"},"user-object":{"macs":["'$mac0'","'$mac1'"]}}' >msgs/tx/svc
fi
